"""
类的多态 (Polymorphism)
====================

1. 什么是多态？
----------------
多态指的是，同一个行为，使用不同的对象获得不同的状态。
如，定义函数（方法），通过类型注解声明需要父类对象，实际传入子类对象进行工作，从而获得不同的工作状态。

多态是面向对象编程的重要特性之一，它允许我们使用统一的接口来操作不同的对象类型，
从而实现代码的灵活性和可扩展性。

2. 什么是抽象类（接口）
------------------------
包含抽象方法的类，称之为抽象类。抽象方法是指：没有具体实现的方法（pass）称之为抽象方法。

在Python中，可以通过继承ABC类并使用@abstractmethod装饰器来定义抽象类和抽象方法。

3. 抽象类的作用
----------------
- 多用于做顶层设计（设计标准），以便子类做具体实现
- 也是对子类的一种软性约束，要求子类必须复写（实现）父类的一些方法
- 并配合多态使用，获得不同的工作状态

抽象类不能被直接实例化，只能被继承，强制子类实现特定的方法。
"""

# 导入ABC相关模块
from abc import ABC, abstractmethod
from typing import List

# 1. 抽象类的定义
class Animal(ABC):
    """动物抽象类"""
    
    def __init__(self, name: str):
        self.name = name
    
    @abstractmethod
    def make_sound(self) -> None:
        """抽象方法：发出声音"""
        pass
    
    @abstractmethod
    def move(self) -> None:
        """抽象方法：移动"""
        pass
    
    # 抽象类中也可以有具体实现的方法
    def sleep(self) -> None:
        """具体方法：睡觉"""
        print(f"{self.name} 正在睡觉")

# 2. 具体子类的实现
class Dog(Animal):
    """狗类"""
    
    def make_sound(self) -> None:
        print(f"{self.name} 汪汪叫")
    
    def move(self) -> None:
        print(f"{self.name} 四肢奔跑")

class Cat(Animal):
    """猫类"""
    
    def make_sound(self) -> None:
        print(f"{self.name} 喵喵叫")
    
    def move(self) -> None:
        print(f"{self.name} 蹑手蹑脚地走")

class Bird(Animal):
    """鸟类"""
    
    def make_sound(self) -> None:
        print(f"{self.name} 叽叽喳喳")
    
    def move(self) -> None:
        print(f"{self.name} 在天空中飞翔")

# 3. 多态的使用
def animal_behavior(animal: Animal) -> None:
    """
    展示动物行为的函数，体现多态
    
    Args:
        animal (Animal): 动物对象（可以是任何Animal的子类实例）
    """
    animal.make_sound()  # 同样的方法调用，不同的行为
    animal.move()        # 同样的方法调用，不同的行为
    animal.sleep()       # 调用父类的具体方法

# 4. 多态的实际应用示例
# 创建不同的动物对象
dog = Dog("旺财")
cat = Cat("咪咪")
bird = Bird("小鸟")

# 使用多态调用相同的方法，但获得不同的行为
animal_behavior(dog)
animal_behavior(cat)
animal_behavior(bird)

# 5. 更复杂的多态示例 - 图形绘制
class Shape(ABC):
    """图形抽象类"""
    
    @abstractmethod
    def area(self) -> float:
        """计算面积的抽象方法"""
        pass
    
    @abstractmethod
    def perimeter(self) -> float:
        """计算周长的抽象方法"""
        pass
    
    def description(self) -> str:
        """具体方法：描述图形"""
        return f"这是一个{self.__class__.__name__}"

class Rectangle(Shape):
    """矩形类"""
    
    def __init__(self, width: float, height: float):
        self.width = width
        self.height = height
    
    def area(self) -> float:
        return self.width * self.height
    
    def perimeter(self) -> float:
        return 2 * (self.width + self.height)

class Circle(Shape):
    """圆形类"""
    
    def __init__(self, radius: float):
        self.radius = radius
    
    def area(self) -> float:
        return 3.14159 * self.radius ** 2
    
    def perimeter(self) -> float:
        return 2 * 3.14159 * self.radius

# 使用多态处理不同的图形
def print_shape_info(shape: Shape) -> None:
    """
    打印图形信息的函数，体现多态
    
    Args:
        shape (Shape): 图形对象
    """
    print(shape.description())
    print(f"面积: {shape.area():.2f}")
    print(f"周长: {shape.perimeter():.2f}")
    print("-" * 20)

# 创建不同的图形对象
# rectangle = Rectangle(5, 3)
# circle = Circle(4)
# 
# # 使用多态调用相同的方法，但获得不同的行为
# print_shape_info(rectangle)
# print_shape_info(circle)

# 6. 多态与容器的结合使用
def process_shapes(shapes: List[Shape]) -> None:
    """
    处理图形列表，体现多态
    
    Args:
        shapes (List[Shape]): 图形对象列表
    """
    total_area = 0
    for shape in shapes:
        # 多态：同样的方法调用，不同的行为
        print(shape.description())
        area = shape.area()
        perimeter = shape.perimeter()
        print(f"  面积: {area:.2f}")
        print(f"  周长: {perimeter:.2f}")
        total_area += area
    print(f"总面积: {total_area:.2f}")

# 使用示例
# shapes = [Rectangle(3, 4), Circle(2), Rectangle(5, 2), Circle(3)]
# process_shapes(shapes)

# 多态的注意事项：
"""
1. 多态依赖于继承和方法重写
2. 父类定义接口（抽象方法），子类实现具体行为
3. 通过父类引用调用方法时，实际执行的是子类的方法
4. 多态提高了代码的可扩展性和可维护性
5. 抽象类不能被实例化，只能被继承
"""

# 多态的优势：
"""
1. 代码复用：使用统一的接口处理不同的对象
2. 可扩展性：添加新的子类不需要修改使用多态的代码
3. 可维护性：修改某个子类的实现不会影响其他代码
4. 灵活性：运行时决定调用哪个具体的方法实现
"""